/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// nx::pointer
#include "memory/pointer.h"
// nx_types
#include "type/typelist.h"
// nx_no_qualifier
#include "type/typedetect.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

namespace format
{
    /*
        format types
    */

    typedef nx_types
    (
        bool, 
        char,  uchar,  wchar,
        short, ushort, int,    uint,
        long,  ulong,  llong,  ullong,
        float, double, ldouble,
        pchar, pwchar, pvoid
    ) types_t;

    static const char* (LIST[]) = 
    {
        "",     // formated str of 'bool' is "true" or "false"
        "%c",   "%c",   "%lc",
        "%d",   "%u",   "%d",   "%u",
        "%ld",  "%lu",  "%lld", "%llu",
        "%f",   "%lf",  "%.17lf",
        "%s",   "%ls",  "%p"
    };

    template <typename T>
    struct Check
    {
        template <typename U, typename Foo_ = type::null_t>
        struct detail_
            : type::Int
              <
                  nx_is_point(U) ? nx_types_find(types_t, pvoid) :
                                   nx_types_find(types_t, U)
              >
        {};

        template <typename Foo_>
        struct detail_<pchar, Foo_>
            : type::Int<nx_types_find(types_t, pchar)>
        {};

        template <typename Foo_>
        struct detail_<pwchar, Foo_>
            : type::Int<nx_types_find(types_t, pwchar)>
        {};

        NX_VALUE(
            int, 
            nx_value(
                detail_
                <
                    typename nx_no_qualifier(
                    typename nx_select(
                             nx_is_array(T),
                    typename nx_no_array(T)*, T))
                >
            )
        );
    };

    template <typename T> const char* check(void)     { return (LIST[nx_value(Check<T>)]); }
    template <typename T> const char* check(T&)       { return check<T>(); }
    template <typename T> const char* check(const T&) { return check<T>(); }

    /*
        format policys
    */

    struct by_cout
    {
        static void out(const char* str)
        {
            std::cout << str;
        }
    };

    struct by_cerr
    {
        static void out(const char* str)
        {
#       if defined(NX_OS_WIN)
            ::OutputDebugStringA(str);
#       endif
            std::cerr << str;
        }
    };

    struct by_clog
    {
        static void out(const char* str)
        {
            std::clog << str;
        }
    };

    /*
        format out
    */

    template <typename Model>
    void out(const char* fmt, ...)
    {
        if (!fmt) return;

        std::va_list args;
        va_start(args, fmt);

        pointer<void> ptr(/*std::*/vsnprintf(NULL, 0, fmt, args) + 1);
        char* buf = pointer_cast<char>(ptr);
        if (buf)
        {
            std::vsprintf(buf, fmt, args);
            Model::out(buf);
        }

        va_end(args);
    }
}

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
